﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Reflection;
using System.Text;
using Cloak.Globalization;

namespace Ruleweaver.Rules.CheckMethods
{
	[ContractClass(typeof(SequenceCheckMethodContract))]
	public abstract class SequenceCheckMethod : CheckMethod
	{
		protected override bool SupportsTargetType(Type targetType)
		{
			return IsSequenceType(targetType);
		}

		protected override Type GetEffectiveTargetType(Type targetType)
		{
			return typeof(IEnumerable<>).MakeGenericType(GetElementType(targetType));
		}

		protected override MethodInfo GetMethod(Type targetType, Type checkType)
		{
			return GetMethod(GetElementType(targetType), targetType, checkType);
		}

		protected abstract MethodInfo GetMethod(Type elementType, Type targetType, Type checkType);

		[Pure]
		protected static bool IsSequenceType(Type targetType)
		{
			Contract.Requires(targetType != null);

			return IsSequenceType(targetType, typeof(IEnumerable<>));
		}

		[Pure]
		protected static bool IsSequenceType(Type targetType, Type openSequenceType)
		{
			Contract.Requires(targetType != null);
			Contract.Requires(openSequenceType != null);

			return targetType.IsGenericType && openSequenceType.IsAssignableFrom(targetType.GetGenericTypeDefinition());
		}

		[Pure]
		protected static Type GetElementType(Type targetType)
		{
			Contract.Requires(targetType != null);
			Contract.Requires(IsSequenceType(targetType));

			return targetType.GetGenericArguments().First();
		}
	}

	[ContractClassFor(typeof(SequenceCheckMethod))]
	internal abstract class SequenceCheckMethodContract : SequenceCheckMethod
	{
		protected override MethodInfo GetMethod(Type elementType, Type targetType, Type checkType)
		{
			Contract.Requires(elementType != null);
			Contract.Requires(targetType != null);
			Contract.Requires(checkType != null);
			Contract.Ensures(Contract.Result<MethodInfo>() != null);

			return null;
		}
	}
}